#pragma once

#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <filesystem>
#include <sys/stat.h>
#include <memory>
#include <sstream>

namespace cloud
{
    namespace fs = std::filesystem;

    // 对文件操作的工具类
    class FileUtil
    {
    private:
        std::string filename_;

    public:
        FileUtil(const std::string &filename) : filename_(filename) {}
        ~FileUtil() {}

        // 获取文件大小
        int64_t FileSize()
        {
            struct stat st;
            if (stat(filename_.c_str(), &st) < 0)
            {
                std::cout << "get file size failed !" << std::endl;
                return -1;
            }

            return st.st_size;
        }

        // 删除文件
        bool Remove()
        {
            if (!this->Exists())
            {
                return true;
            }

            remove(filename_.c_str());
            return true;
        }

        // 获取文件最近一次的修改时间
        time_t LastModifyTime()
        {
            struct stat st;
            if (stat(filename_.c_str(), &st) < 0)
            {
                std::cout << "get file last modify time failed !" << std::endl;
                return -1;
            }

            return st.st_mtime;
        }

        // 获取文件最近一次被访问的时间
        time_t LastVisitTime()
        {
            struct stat st;
            if (stat(filename_.c_str(), &st) < 0)
            {
                std::cout << "get file last visit time failed !" << std::endl;
                return -1;
            }

            return st.st_atime;
        }

        // 获取文件名称 如：./code/hello.txt，就是获取出 hello.txt
        std::string FileName()
        {
            size_t pos = filename_.find_last_of("\\");
            if (pos == std::string::npos)
            {
                return filename_;
            }

            return filename_.substr(pos + 1);
        }

        // 从string指定位置pos, 获取指定长度len的数据
        bool GetPosLen(std::string *content, size_t pos, size_t len)
        {
            size_t fsize = this->FileSize();
            if (pos + len > fsize)
            {
                std::cout << "get file length error!" << std::endl;
                return false;
            }

            std::ifstream ifs;
            ifs.open(filename_, std::ios::binary);
            if (!ifs.is_open())
            {
                std::cout << "read open file failed!" << std::endl;
                return false;
            }

            ifs.seekg(pos, std::ios::beg); // 从开始位置，直接跳转到指定位置
            content->resize(len);
            ifs.read(&(*content)[0], len);
            if (ifs.good() == false)
            {
                std::cout << "read file content failed!" << std::endl;
                ifs.close();
                return false;
            }
            ifs.close();

            return true;
        }

        // 获取文件的所有数据内容
        bool GetContent(std::string *content)
        {
            size_t fsize = this->FileSize();
            return GetPosLen(content, 0, fsize);
        }

        // 向文件中写入数据内容
        bool SetContent(const std::string &content)
        {
            std::ofstream ofs;
            ofs.open(filename_, std::ios::binary);
            if (!ofs.is_open())
            {
                std::cout << "write open file content failed!" << std::endl;
                return false;
            }

            ofs.write(&content[0], content.size());
            if (ofs.good() == false)
            {
                std::cout << "write file content failed!" << std::endl;
                ofs.close();
                return false;
            }
            ofs.close();

            return true;
        }

        // 判断目录/文件是否存在
        bool Exists()
        {
            return fs::exists(filename_);
        }

        // 创建目录
        bool CreateDirectory()
        {
            if (this->Exists())
                return true;

            return fs::create_directories(filename_);
        }

        // 浏览目录，即查看目录有哪些文件
        bool ScanDirectory(std::vector<std::string> *arry)
        {
            for (auto &p : fs::directory_iterator(filename_))
            {
                if (fs::is_directory(p))
                    continue;

                // relative_path() ：带有路径的文件名
                arry->push_back(fs::path(p).relative_path().string());
            }

            return true;
        }
    };
}